/*
 * rpc.h - RPC related operations
 *
 * Authors Mohamed Karaoui (2015)
 *         Alain Greiner (2016)
 *
 * Copyright (c) UPMC Sorbonne Universites
 * 
 * This file is part of ALMOS-MK.
 *
 * ALMOS-MK is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MK is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MK; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef _RPC_H_
#define _RPC_H_

#include <errno.h>
#include <types.h>
#include <bits.h>
#include <atomic.h>
#include <spinlock.h>
#include <remote_fifo.h>
#include <kernel_config.h>

/**********************************************************************************/
/**************  structures for Remote Procedure Calls ****************************/
/**********************************************************************************/

/***********************************************************************************
 *  This enum defines all RPC indexes (must be consistent with array in rpc.c)
 **********************************************************************************/

typedef enum
{
    RPC_PMEM_GET_PAGES         = 0,
    RPC_PROCESS_PID_ALLOC      = 1,
    RPC_PROCESS_MIGRATE        = 2,
    RPC_THREAD_USER_CREATE     = 3,
    RPC_VFAT_ALLOC_FAT_ENTRY   = 4,
    RPC_VFAT_CLUSTER_COUNT     = 5,
    RPC_VFS_LOOKUP_CHILD       = 6,
    RPC_MAX_INDEX              = 7
}
rpc_index_t;

/***********************************************************************************
 *  This structure defines the RPC descriptor
 **********************************************************************************/

typedef struct rpc_desc_s
{
	rpc_index_t       index;      // index of requested RPC service
	volatile uint32_t response;   // response valid when 0
    uint64_t          args[7];    // input/output arguments buffer
} 
rpc_desc_t;

/***********************************************************************************
 *  This structure defines the RPC fifo, the associated light lock,
 *  and the intrumentation counter.
 **********************************************************************************/

typedef struct rpc_fifo_s
{
	spinlock_t lock;          // spinlock for the readers
	uint64_t          count;  // total number of received RPCs (instrumentation) 
	remote_fifo_t     fifo;   // embedded remote fifo
} 
rpc_fifo_t;


/**********************************************************************************/
/******* Generic functions supporting RPCs : client side **************************/
/**********************************************************************************/

/***********************************************************************************
 * This blocking function executes on the client core. 
 * It puts one RPC extended pointer in the remote fifo. 
 * It sends an IPI if fifo is empty, and waits until RPC response available.
 * The RPC descriptor must be allocated in the caller's stack
 * and initialised by the caller.  Exit with a Panic message if remote fifo 
 * is still full after (CONFIG_RPC_PUT_MAX_ITERATIONS) retries.
 * @ cxy   : server cluster identifier
 * @ desc  : local pointer on RPC descriptor in client cluster
 **********************************************************************************/
void rpc_send_sync( cxy_t        cxy,    
                    rpc_desc_t * desc );

/***********************************************************************************
 * This non blocking function is called by the scheduler of the client core
 * to detect RPC completion.  
 * @ ptr   : local pointer on the RPC descriptor in client cluster.
 * @ return true if the client thread must resume (RPC completed). 
 **********************************************************************************/ 
static bool_t rpc_callback( void * ptr );



/**********************************************************************************/
/******* Generic functions supporting RPCs : server side **************************/
/**********************************************************************************/

/***********************************************************************************
 * This function initialises the local RPC fifo and the lock protecting readers.
 * The number of slots is defined by the CONFIG_REMOTE_FIFO_SLOTS parameter.
 * Each slot contains an extended pointer on the RPC descriptor.
 * @ rf     : pointer on the local RPC fifo.
 **********************************************************************************/ 
void rpc_fifo_init( rpc_fifo_t * rf );

/***********************************************************************************
 * This function is the entry point for RPC handling on the server side.
 * It can be executed by any thread running (in kernel mode) on any CPU.
 * It first checks the CPU private RPC fifo, an then the cluster shared RPC fifo.
 * It calls the rpc_activate_thread() function to activate a dedicated RPC thread,
 * after registration of the selected RPC fifo in the selected thread descriptor. 
 * @ returns true if at least one RPC found / false otherwise.
 **********************************************************************************/
bool_t rpc_check();

/***********************************************************************************
 * This function contains the loop to execute all pending RPCs on the server side.
 * It should be called with irq disabled and after light lock acquisition.
 * @ rpc_fifo  : pointer on the local RPC fifo
 * @ returns 0 if success
 **********************************************************************************/
error_r rpc_execute_all( rpc_fifo_t * rpc_fifo );

/***********************************************************************************
 * This function is acting as a demultiplexor on the server side. 
 * Depending on the RPC index stored in the RPC descriptor,
 * it calls the relevant rpc_*_server() function.
 * @ xptr : extended pointer on the RPC descriptor.
 **********************************************************************************/
void rpc_handle( xptr_t * xp );

/**********************************************************************************
 * This function activates one RPC thread to handle the pending RPC(s) contained
 * in one RPC fifo (shared or private). It is called by any thread detecting
 * a non-empty RPC fifo.
 * It gets the first free RPC thread from the free-list, or creates a new one
 * if the RPC thread free-list associated to the current cpu is empty. 
 * Finally, the calling thread immediately switches to the RPC thread.
 * @ rpc_fifo : pointer on the non-empty RPC fifo.
 * @ return 0 if success / return ENOMEM if error.
 **********************************************************************************/
error_t rpc_activate_thread( rpc_fifo_t * rpc_fifo );

/***********************************************************************************
 * This function defines the infinite loop executed by each RPC thread.
 **********************************************************************************/
void * rpc_thread();

/**********************************************************************************
 * This call-back function must be registered in the RPC thread descriptor.
 * It is executed by the scheduler when the thread makes a sched_sleep() call,
 * when the "before_sleep" field in thread descriptor is not NULL. 
 * It releases the light lock and print a debug message.
 * @ thread  : local pointer on thread. 
 **********************************************************************************/
void rpc_before_sleep( thread_t * thread );

/**********************************************************************************
 * This call-back function must be registered in the RPC thread descriptor.
 * It is executed by the scheduler when the thread makes a wakeup(),
 * when the "after_wakeup" field in thread descriptor is not NULL. 
 * It print a debug message.
 * @ thread  : local pointer on thread. 
 **********************************************************************************/
void rpc_after_wakeup( thread_t * thread );

/**********************************************************************************/
/******* Marshalling functions attached to the various RPCs ***********************/
/**********************************************************************************/

/***********************************************************************************
 * The RPC_PMEM_GET_PAGES allocates one or several pages in a remote cluster,
 * and returns the PPN of the first allocated page.
 * @ cxy     : server cluster identifier
 * @ order   : [in]  ln2( number of requested pages )
 * @ status  : [out] error status (0 if success)
 * @ ppn     : [out] first physical page number
 **********************************************************************************/
void rpc_pmem_get_pages_client( cxy_t      cxy,
                                uint32_t   order,
                                uint32_t * status,
                                uint32_t * ppn );

void rpc_pmem_get_pages_server( xptr_t xp );

/***********************************************************************************
 * The RPC_PROCESS_PID_ALLOC allocates one new PID in a remote cluster, registers
 * the new process in the remote cluster, and returns the PID, and an error code.
 * @ cxy     : server cluster identifier.
 * @ process : [in]  pointer on process descriptor in client cluster.
 * @ status  : [out] error status (0 if success).
 * @ pid     : [out] new process identifier.
 **********************************************************************************/
void rpc_process_pid_alloc_client( cxy_t       cxy,
                                   process_t * process,
                                   error_t   * status,
                                   pid_t     * pid );

void rpc_process_pid_alloc_server( xptr_t xp );

/***********************************************************************************
 * The RPC_PROCESS_MIGRATE creates a process descriptor copy, in a remote cluster
 * and initializes if from information found in the reference process descriptor.
 * This remote cluster becomes the new reference cluster.
 * @ cxy     : server cluster identifier.
 * @ xp_ref  : [in]  extended pointer on reference process descriptor.
 * @ status  : [out] error status (0 if success).
 **********************************************************************************/
void rpc_process_migrate_client( cxy_t       cxy,
                                 xptr_t      xp_ref,
                                 error_t   * status );

void rpc_process_migrate_server( xptr_t xp );

/***********************************************************************************
 * The RPC_VFS_LOOKUP_CHILD returns in the lpr structure information on the
 * the inode identified by the dentry name and the lkp structure. 
 * @ cxy     : server cluster identifier
 * @ lkp     : [in]   pointer on input structure in client cluster
 * @ name    : [in]   dentry name
 * @ lpr     : [out]  pointer on output structure in client cluster
 **********************************************************************************/
void rpc_vfs_lookup_child_client( cxy_t                    cxy,
                                  vfs_lookup_t          *  lkp,
                                  char                  *  name,
                                  vfs_lookup_response_t *  lpr );

void rpc_vfs_lookup_child_server( xptr_t xp );

/***********************************************************************************
 * The RPC_THREAD_USER_CREATE creates an user thread in the server cluster,
 * as specified by the pthread_attr_t argument. It returns an extended pointer
 * on the thread descriptor in server cluster and an error code.
 * @ cxy     : server cluster identifier.
 * @ attr    : [in]   pointer on pthread_attr_t in client cluster.
 * @ error   : [out] error status (0 if success).
 * @ thread  : [out] extended pointer on thread descriptor. 
 **********************************************************************************/
void rpc_thread_user_create_client( cxy_t             cxy,
                                    pthread_attr_t *  attr,
                                    error_t        *  error,
                                    xptr_t         *  new_thread );

void rpc_thread_user_create_server( xptr_t xp );



#endif
